home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-04
/
m2ticker.zip
/
PIT.MOD
< prev
Wrap
Text File
|
1991-12-01
|
15KB
|
364 lines
(* Sound and Delay routines for Fitted Software Tools Modula-2 compiler.
The Delay, Sound, and NoSound procedures are functionally
identical to their Turbo Pascal namesakes.
These routines directly address the Intel 8253 Programmable
Interval Timer (PIT) chip. Therefore these routines should run
faithfully on all PC-compatible systems, regardless of the CPU
or clock speed. I wrote these procedures using a a 12MHz 0ws
'286, and tested them on an 8MHz 8088.
Your shareware contribution appreciated by the author:
G.S.Vigneault,
Box 7169, Station A,
Toronto, Ontario,
Canada M5W 1X8
Send comments to...
UUCP: gregsv@eastern.uucp
greg.vigneault@canrem.uucp
ZLink: ACCESSCOMM
*)
MODULE PIT;
FROM InOut IMPORT Write, WriteLn, WriteLongCard, WriteString;
FROM Keyboard IMPORT GetKeyCh, KeyPressed;
FROM SYSTEM IMPORT ASSEMBLER;
(*------------------------------------*)
PROCEDURE Set8253( count : CARDINAL );
(* The 8253 counter #0 is the heartbeat of the DOS clock. The BIOS loads
counter #0 with 0, mode 3. Some hi-res timing routines may leave it in
mode 2, which will affect these procedures. Use Set8253(0) at the very
beginning of your program to load counter#0 the same way that the BIOS
does at power up. (Sloppy TSRs might still interfere.) *)
BEGIN
ASM
MOV CX, count (* count down from this *)
MOV DX,40H (* 8253 counter#0 port *)
CLI (* do not disturb *)
MOV AL,36H (* counter 0, mode 3, RL=11 *)
OUT 43H,AL (* to 8253 control reg *)
MOV AL,CL
OUT DX,AL (* write counter 0, LSB *)
MOV AL,CH
OUT DX,AL (* write counter 0, MSB *)
STI
MOV ES,DX (* far data segment *)
MOV AX,ES:[006CH] (* get BIOS timer word *)
wait: CMP AX,ES:[006CH] (* wait for next tick *)
JE wait (* to synchronize *)
END
END Set8253;
(*------------------------------------*)
PROCEDURE ReadTimer( VAR count : LONGCARD );
(* Returns a 32-bit representation of 8253 counter#0. By calling this
routine multiple times you may determine the elapsed time between each
call, approximately in _microseconds_ (µs). *)
BEGIN
ASM
MOV DX,40H (* 8253 count#0 port *)
MOV ES,DX (* far data segment *)
CLI (* do not disturb *)
MOV AL,06H (* counter0, RL=00 to latch *)
OUT 43H,AL (* control reg: mode 3 *)
IN AL,DX (* get counter#0 LSB *)
MOV BL,AL (* save it *)
IN AL,DX (* get counter#0 MSB *)
STI (* interrupts okay now *)
MOV BH,AL (* BX = counter#0 *)
MOV AX,ES:[006CH] (* get counter#0 overflow *)
NEG BX (* downcounter to upcounter *)
LES DI, count (* pointer to longcard *)
MOV ES:[DI],BX (* lower16 = counter#0 *)
MOV ES:[DI+2],AX (* upper16 = BIOS word *)
END
END ReadTimer;
(*------------------------------------*)
PROCEDURE Delay( ms : CARDINAL );
(* Delays (pauses) approximately ms milliseconds before returning. I find
this to be accurate to 0.2%. You may fine-tune this procedure to your
system by adjusting the original "MOV DI,1990" load value below. *)
BEGIN
ASM
MOV CX,ms (* get the requested ms wait *)
JCXZ exit (* if nothing to do *)
MOV DI,1990 (* this many is ~1ms *)
MOV DX,40H (* counter 0 reg port *)
wrap: CLI
MOV AL,06H (* counter0, RL=00 to latch *)
OUT 43H,AL (* control reg: mode 3 *)
IN AL,DX (* get counter0 LSB *)
MOV AH,AL (* save it *)
IN AL,DX (* get counter0 MSB *)
STI (* because of 8253 design *)
XCHG AL,AH (* AX = counter 0 *)
CMP AX,DI (* too near end of count? *)
JBE wrap (* wait (less than 1ms) *)
MOV SI,AX (* save ref *)
wait: CLI (* _must_ read 2 bytes *)
MOV AL,06H (* counter0, RL=00 to latch *)
OUT 43H,AL (* control reg: mode 3 *)
IN AL,DX (* get counter0 LSB *)
MOV BL,AL (* save it *)
IN AL,DX (* get counter0 MSB *)
STI (* interrupts okay now *)
MOV BH,AL (* now BX = counter 0 *)
MOV AX,SI (* get ref val *)
SUB AX,BX (* calc difference *)
CMP AX,DI (* waited long enough? *)
JB wait (* wait for 1ms *)
LOOP wrap (* loop for another 1ms *)
exit:
END
END Delay;
(*------------------------------------*)
PROCEDURE Sound( Freq : CARDINAL );
(* tone of Freq frequency will continue to play until NoSound is called *)
BEGIN
Freq := SHORT( 1193182L DIV LONG( Freq ));
ASM
MOV DX,043H (* 8253 control reg port *)
MOV AL,0B6H (* RL=11, counter#2, mode 3 *)
OUT DX,AL
DEC DX (* 42H = 8253 counter#2 *)
MOV AX, Freq
CLI (* do not disturb! *)
OUT DX,AL (* Freq LSB *)
MOV AL,AH
OUT DX,AL (* Freq MSB *)
STI (* due to 8253 design *)
MOV DX,061H (* 8255 port B *)
IN AL,DX (* D0=counter#2 gate enable *)
OR AL,3 (* D1=speaker enable *)
OUT DX,AL (* set bits D0 and D1 *)
END
END Sound;
(*------------------------------------*)
PROCEDURE NoSound;
(* stops Sound by disabling speaker, and stopping 8253 counter 2 *)
BEGIN
ASM
MOV DX,061H (* 8255 port B *)
IN AL,DX
AND AL,0FCH (* bits D0 and D1 off *)
OUT DX,AL
END
END NoSound;
(*------------------------------------*)
PROCEDURE Tone( Freq, dur : CARDINAL);
(* plays Freq tone for dur jiffies (1 PC jiffy = 1/18 sec ) *)
BEGIN
Freq := SHORT( 1193182L DIV LONG( Freq ));
ASM
MOV DX,043H (* 8253 control reg *)
MOV AL,0B6H
OUT DX,AL
DEC DX (* 42H = 8253 counter 2 *)
MOV AX, Freq (* freq divider *)
CLI (* do not disturb! *)
OUT DX,AL (* counter#2 LSB *)
MOV AL,AH
OUT DX,AL (* counter#2 MSB *)
STI (* due to 8253 design *)
MOV DX,061H (* 8255 Port B *)
IN AL,DX
OR AL,3 (* enable counter2 and spkr *)
OUT DX,AL
SUB AH,AH (* get system jiffy count *)
INT 1AH (* via BIOS *)
ADD DX, dur (* calc requested delay val *)
MOV BX,DX (* save *)
cont:
INT 1AH (* get current jiffies *)
CMP DX,BX (* are we there yet? *)
JNE cont (* loop til we are *)
MOV DX,061H (* port B bits *)
IN AL,DX
AND AL,0FCH (* disable counter2 and spkr *)
OUT DX,AL
END
END Tone;
(*------------------------------------*)
PROCEDURE GetVideoMode( VAR vmode : CARDINAL );
BEGIN
ASM
MOV AH,0FH (* get video mode fn *)
INT 10H (* via BIOS *)
SUB AH,AH (* make it a word *)
LES DI, vmode (* pointer to the variable *)
MOV ES:[DI],AX (* update vmode *)
END
END GetVideoMode;
(*------------------------------------*)
PROCEDURE SetVideoMode( Vmode : CARDINAL );
BEGIN
ASM
MOV AX, Vmode (* get requested vid mode *)
SUB AH,AH (* set video mode fn *)
INT 10H (* via BIOS *)
END
END SetVideoMode;
(*------------------------------------*)
PROCEDURE ClearScreen;
(* text mode clear screen *)
BEGIN
ASM
SUB CX,CX (* upper left x1,y1 := 0 *)
MOV DL,79 (* x2 lower right *)
MOV DH,24 (* y2 lower right *)
MOV BH,7 (* attribute *)
SUB AL,AL (* to blank entire window *)
MOV AH,6 (* using scroll *)
INT 10H (* via BIOS *)
END
END ClearScreen;
(*------------------------------------*)
PROCEDURE GotoXY( Xcord, Ycord : CARDINAL );
(* to place the text mode cursor *)
BEGIN
ASM
MOV DX, Xcord
MOV AX, Ycord
MOV DH,AL
SUB BH,BH (* display page 0 *)
MOV AH,2 (* set cursor position *)
INT 10H (* via BIOS *)
END
END GotoXY;
(*----------------------------------------------------------------------*)
VAR
ch : CHAR;
frequency,
OldVmode,
duration : CARDINAL;
time1, time2 : LONGCARD;
BEGIN (* main *)
Set8253( 0 ); (* make sure it's the BIOS default *)
GetVideoMode( OldVmode ); (* save current video mode *)
SetVideoMode( 2 ); (* set 80x25 B&W text *)
ClearScreen; (* blank the display *)
Sound( 440 );
FOR duration := 1 TO 5
DO
ReadTimer( time1 ); (* to time the Delay proc *)
Delay(1000); (* 1s = 1000ms = 1000000µs *)
ReadTimer( time2 ); (* for elapsed microseconds *)
WriteString("[Timed Delay(1000ms) of ");
WriteLongCard( time2-time1, 7 ); (* delta ReadTimer *)
WriteString("µs (microseconds)]");
WriteLn;
WriteLn;
END;
NoSound;
WriteLn;
WriteString("Press any key to continue...");
REPEAT (* wait *) UNTIL KeyPressed();
ClearScreen;
GotoXY( 37, 1 );
WriteString("ORGAN");
GotoXY( 3, 3 );
WriteString(" Use the keys in rows Q..P and A..' to play notes");
GotoXY( 3, 5 );
WriteString(" Use 1..9 keys to set note duration (in 0.054945 sec).");
GotoXY( 3, 7 );
WriteString(" Press X to EXIT.");
WriteLn;
WriteLn;
duration := 5;
LOOP
frequency := 0;
GetKeyCh(ch);
IF ch <> 0C THEN
ch := CAP(ch);
CASE ch OF
"Q" : frequency := 440; |
"W" : frequency := 466; |
"E" : frequency := 494; |
"R" : frequency := 523; |
"T" : frequency := 554; |
"Y" : frequency := 587; |
"U" : frequency := 622; |
"I" : frequency := 659; |
"O" : frequency := 698; |
"P" : frequency := 740; |
"A" : frequency := 784; |
"S" : frequency := 831; |
"D" : frequency := 880; |
"F" : frequency := 932; |
"G" : frequency := 988; |
"H" : frequency := 1046;|
"J" : frequency := 1109;|
"K" : frequency := 1175;|
"L" : frequency := 1245;|
";" : frequency := 1329;|
"'" : frequency := 1397;|
"1".."9"
: duration := ORD(ch)-48;|
"X" : EXIT
ELSE ;
END (*case*)
END; (*if*)
IF frequency >= 100 THEN
Write(ch);
Tone( frequency, duration )
END
END; (*loop*)
SetVideoMode( OldVmode ) (* restore original video mode *)
END PIT.